//-------------------------------------------------------------------
//	Copyright (c) 2013  Zhirnov Andrey
//	This file is part of the "Noise Library" project.
//	See copyright notice in "linNoiseCL.h".
//-------------------------------------------------------------------

#pragma once

#include "../CSource.h"
#include "CSourceOutput.h"

namespace Noise
{
	
	//
	// Function Source for swizzling output color
	//

	class CSourceSwizzle : public ISourceOutput
	{
	// types
	private:
		typedef CSourceSwizzle	Self;


	// variables
	protected:
		CSourcePtr		_source;
		u8_vec4			_swizzle;
		
		static const
			ESource		_sourceType = SWIZZLE;

	// methods
	public:
		CSourceSwizzle () : _swizzle(0, 1, 2, 3)
		{}

		~CSourceSwizzle ()
		{}

		ESourceState GetState () const
		{
			return _source.IsNotNull() ? SOURCE_COMPLETE : UNCOMPLETE_ATTACHMENT_SOURCE;
		}
		
		virtual void Delete ()
		{
			_source	= null;
		}

		virtual EResult GetInteger (ESourceQuery type, int *v, int count) const
		{
			CHECK_ARGUMENT_R( count >= 1, INVALID_VALUE );

			switch ( type )
			{
				case SOURCE_STATE :
					v[0] = GetState();
					return OK;

				case SOURCE_TYPE :
					v[0] = _sourceType;
					return OK;

				case SOURCES_COUNT : {
					int	count = 0;
					_source->GetInteger( type, &count, 1 );
					v[0] = count + 1;
					return OK;
				}

				case INPUT_SOURCES :
					v[0] = 1;
					return OK;
			}
			return INVALID_ENUM;
		}
		
		virtual EResult SetArg (EArgument type, CSourcePtr src)
		{
			switch ( type )
			{
				case SOURCE_0 : {
					if ( not HasColorOutput( src ) )
						return INVALID_SOURCE;

					_source = src;
					return OK;
				}
			}
			return INVALID_ENUM;
		}

		virtual EResult SetArg (EArgument type, const int *v, int count)
		{
			CHECK_ARGUMENT_R( count >= 4, INVALID_VALUE );

			switch ( type )
			{
				case ARG_SWIZZLE :
					FOR( i, _swizzle ) {
						_swizzle[i] = v[i] & 3;
					}
					return OK;
			}
			return INVALID_ENUM;
		}

		virtual EResult GetArg (EArgument type, CSourcePtr &src) const
		{
			switch ( type )
			{
				case SOURCE_0 :	src = _source;	return OK;
			}
			return INVALID_ENUM;
		}
		
		virtual EResult GetArg (EArgument type, int *v, int count) const
		{
			CHECK_ARGUMENT_R( count >= 4, INVALID_VALUE );

			switch ( type )
			{
				case ARG_SWIZZLE :
					FOR( i, _swizzle ) {
						v[i] = _swizzle[i];
					}
					return OK;
			}
			return INVALID_ENUM;
		}
		
		virtual CSourcePtr Clone () const override
		{
			Self * result = new Self();
			result->_swizzle = this->_swizzle;
			return result;
		}
		
		virtual void PreBuild (const TPreBuildData &data) override
		{
			data.Push( _source.ptr() );
		}

		virtual bool BuildFunction (TFunctionData &data) override
		{
			data.source
				<< "real4 " << CSource::FunctionName() << _id << " (real3 vPoint)\n{\n"
				<< "const uint4 swizzle = (uint3)( " << _swizzle[0] << ", " << _swizzle[1]
				<< ", " << _swizzle[2] << ", " << _swizzle[3] << " );\n"
				<< "const real4 color = ";
			CSource::BuildFunctionCall( data.source, _source->GetSourceId() ) << ";\n"
				<< "return shuffle(color, swizzle);\n"
				<< "}\n";

			return true;
		}
		
		NATIVE(
		virtual float32 NativeFunction32 (const fvec3 &vPoint) override
		{
			WARNING( "this function is not supported, use NativeFunction4" );
			return 0;
		}

		virtual float64 NativeFunction64 (const dvec3 &vPoint) override
		{
			WARNING( "this function is not supported, use NativeFunction4" );
			return 0;
		}

		template < typename Real, TVec<Real,4> (ISourceOutput::*NativeFunction4)(const TVec<Real,3> &) >
		TVec<Real,4> NativeFunction4 (const TVec<Real,3> &vPoint)
		{
			typedef TVec<Real,4>	Real4;
			
			Real4 color = (_source.To<ISourceOutputPtr>().ptr()->*NativeFunction4)( vPoint );
			return color.Swizzle( _swizzle );
		}
		
		virtual fvec4 NativeFunction4_32 (const fvec3 &vPoint) override
		{
			return NativeFunction4< float32, &ISourceOutput::NativeFunction4_32 >( vPoint );
		}
		
		virtual dvec4 NativeFunction4_64 (const dvec3 &vPoint) override
		{
			return NativeFunction4< float64, &ISourceOutput::NativeFunction4_64 >( vPoint );
		})

		virtual EPixelFormat GetFormat () const override
		{
			return RGBA;
		}

		static bool CreateSource (CSourcePtr &src, CContext *context, ESource type)
		{
			ASSERT( type == _sourceType );

			src = new Self();
			return true;
		}
	};

}	// Noise